package com.gcloud.controller.slb.provider.impl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import org.openstack4j.api.Builders;
import org.openstack4j.model.common.ActionResponse;
import org.openstack4j.model.network.ext.HealthMonitorV2;
import org.openstack4j.model.network.ext.LbMethod;
import org.openstack4j.model.network.ext.LbPoolV2;
import org.openstack4j.model.network.ext.LbProvisioningStatus;
import org.openstack4j.model.network.ext.LoadBalancerV2;
import org.openstack4j.model.network.ext.MemberV2;
import org.openstack4j.model.network.ext.Protocol;
import org.openstack4j.openstack.networking.domain.ext.ListItem;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.gcloud.common.util.StringUtils;
import com.gcloud.controller.provider.NeutronProviderProxy;
import com.gcloud.controller.slb.dao.LoadBalancerDao;
import com.gcloud.controller.slb.dao.VServerGroupDao;
import com.gcloud.controller.slb.entity.LoadBalancer;
import com.gcloud.controller.slb.entity.VServerGroup;
import com.gcloud.controller.slb.model.BackendServer;
import com.gcloud.controller.slb.model.DescribeVServerGroupAttributeResponse;
import com.gcloud.controller.slb.provider.IVServerGroupProvider;
import com.gcloud.core.exception.GCloudException;
import com.gcloud.core.simpleflow.Flow;
import com.gcloud.core.simpleflow.FlowDoneHandler;
import com.gcloud.core.simpleflow.NoRollbackFlow;
import com.gcloud.core.simpleflow.SimpleFlowChain;
import com.gcloud.header.enums.ProviderType;
import com.gcloud.header.enums.ResourceType;
import com.gcloud.header.slb.model.VServerGroupSetType;

import lombok.extern.slf4j.Slf4j;
@Slf4j
@Component
public class NeutronVServerGroupProviderImpl implements IVServerGroupProvider {

	@Autowired
	private NeutronProviderProxy neutronProviderProxy;
	
	@Autowired
	VServerGroupDao vServerGroupDao;
	
	@Autowired
    private LoadBalancerDao lberDao;
	
	public String createVServerGroup(String loadBalancerId, String vServerGroupName, String vServerGroupProtocol) {
		
		LoadBalancer lber = lberDao.getById(loadBalancerId);
		SimpleFlowChain<String, String> chain = new SimpleFlowChain<>("createVServerGroup");
		chain.then(new Flow<String>("createVserverGroup") {
			@Override
			public void run(SimpleFlowChain chain, String data) {
				// TODO Auto-generated method stub
				LbPoolV2 lbPool = neutronProviderProxy.getClient().networking().lbaasV2().lbPool()
						.create(Builders.lbpoolV2().name(vServerGroupName).protocol(Protocol.valueOf(vServerGroupProtocol))
								.lbMethod(LbMethod.ROUND_ROBIN)
								.loadbalancerId(lber.getProviderRefId())
								.adminStateUp(true).build());
				if (lbPool == null) {
					throw new GCloudException("0140104::创建后端服务器组失败");
				}
				chain.data(lbPool.getId());
                chain.next();
			}
			@Override
			public void rollback(SimpleFlowChain chain, String data) {
				// TODO Auto-generated method stub
				 neutronProviderProxy.getClient().networking().lbaasV2().lbPool().delete(data);
				chain.rollback();
			}
		}).then(new NoRollbackFlow<String>("save to db") {

			@Override
			public void run(SimpleFlowChain chain, String data) {
				// TODO Auto-generated method stub
				VServerGroup vServerGroup=new VServerGroup();
				vServerGroup.setProviderRefId(data);
				vServerGroup.setProvider(providerType().getValue());
				vServerGroup.setId(UUID.randomUUID().toString());
				vServerGroup.setLoadBalancerId(loadBalancerId);
				vServerGroup.setName(vServerGroupName);
				vServerGroup.setProtocol(vServerGroupProtocol);
				vServerGroupDao.save(vServerGroup);
				chain.next();
			}
			
		}).done(new FlowDoneHandler<String>() {
            @Override
            public void handle(String data) {
                chain.setResult(data);
            }
        }).start();
		
		if (StringUtils.isNotBlank(chain.getErrorCode())) {
            throw new GCloudException(chain.getErrorCode());
        }
        return chain.getResult();
	}
	
	
	public void setVServerGroupAttribute(String vServerGroupId, String vServerGroupName) {
		LbPoolV2 updated = neutronProviderProxy.getClient().networking().lbaasV2().lbPool().update(vServerGroupId,
				Builders.lbPoolV2Update().name(vServerGroupName).adminStateUp(true).build());
		if (updated == null) {
			throw new GCloudException("更新后端服务器组失败");
		}
	}

	public List<VServerGroupSetType> describeVServerGroups(String loadBalancerId) {
		log.debug("DescribeLbPools begin...");
		Map<String, String> filteringParams = new HashMap<String, String>();
		if (StringUtils.isNotBlank(loadBalancerId)) {
			filteringParams.put("loadbalancer_id", loadBalancerId);
		}
		List<? extends LbPoolV2> pools = neutronProviderProxy.getClient().networking().lbaasV2().lbPool()
				.list(filteringParams);
		List<VServerGroupSetType> vServerGroups = new ArrayList<VServerGroupSetType>();
		if (pools != null && pools.size() > 0) {
			
			for (LbPoolV2 lbPoolV2 : pools) {
				VServerGroupSetType temp = new VServerGroupSetType();
				temp.setvServerGroupId(lbPoolV2.getId());
				temp.setvServerGroupName(lbPoolV2.getName());
				temp.setvServerGroupProtocol(lbPoolV2.getProtocol().name());
				vServerGroups.add(temp);
			}
		}
		log.debug("DescribeLbPools end...");
		return vServerGroups;
	}

	public DescribeVServerGroupAttributeResponse describeVServerGroupAttribute(String vServerGroupId) {
		LbPoolV2 lbPool = neutronProviderProxy.getClient().networking().lbaasV2().lbPool().get(vServerGroupId);
		if (lbPool == null) {
			throw new GCloudException("获取后端服务器组失败");
		}
		DescribeVServerGroupAttributeResponse response =new DescribeVServerGroupAttributeResponse();
		response.setvServerGroupId(lbPool.getId());
		response.setvServerGroupName(lbPool.getName());
		Map<String, String> filter = new HashMap<String, String>();
		/* filter.put("name", "member1"); */
		List<? extends MemberV2> members = neutronProviderProxy.getClient().networking().lbaasV2().lbPool()
				.listMembers(lbPool.getId(), filter);
		if (members != null && members.size() > 0) {
			List<BackendServer> backendServers = new ArrayList<BackendServer>();
			for (MemberV2 memberV2 : members) {
				BackendServer temp = new BackendServer();
				temp.setPort(memberV2.getProtocolPort());
				temp.setServerId(memberV2.getAddress());
				temp.setWeight(memberV2.getWeight());
				temp.setMemberId(memberV2.getId());
				temp.setType("ip");
				backendServers.add(temp);
			}
			response.setBackendServers(backendServers);
		}
		log.debug("DescribeLbPool end...");
		return response;
	}

	public void deleteVServerGroup(String vServerGroupId) {
		boolean hasMonitor = false;
		List<? extends HealthMonitorV2> healthMonitor = neutronProviderProxy.getClient().networking().lbaasV2()
				.healthMonitor().list();
		// 查询pool中对应的healthMonitor
		for (HealthMonitorV2 healthMonitorV2 : healthMonitor) {
			List<ListItem> pools = healthMonitorV2.getPools();
			for (ListItem pool : pools) {
				if (pool.getId().equals(vServerGroupId)) {
					hasMonitor = true;
					break;
				}
			}
			if (hasMonitor) {
				break;
			}
		}

		if (hasMonitor) {
			throw new GCloudException("请先关闭或者删除健康检查");
		}
		
		ActionResponse resp = neutronProviderProxy.getClient().networking().lbaasV2().lbPool().delete(vServerGroupId);
		if (!resp.isSuccess()) {
			throw new GCloudException("删除后端服务器组失败:" + resp.getFault());
		}
	}

	public void addVServerGroupBackendServers(String vServerGroupId,String backendServers) {
		if (StringUtils.isBlank(backendServers)) {
			throw new GCloudException("backendServers is blank");
		}
		
		LbPoolV2 pool = neutronProviderProxy.getClient().networking().lbaasV2().lbPool().get(vServerGroupId);
		String lbId = pool.getLoadbalancers().get(0).getId();
		
		LoadBalancerV2 lb = neutronProviderProxy.getClient().networking().lbaasV2().loadbalancer().get(lbId);
		if(lb == null) {
			throw new GCloudException("负载均衡器不存在");
		}
		String subnetId = lb.getVipSubnetId();
		
		JSONArray jArray = JSONArray.parseArray(backendServers);
		for (int i = 0; i < jArray.size(); i++) {
			JSONObject job = jArray.getJSONObject(i);
			
			waitForLbPendingUpdate(lbId);
			
			String type = job.getString("Type");
			String serverId = job.getString("ServerId");
			if(!type.equals("ip")) {
				throw new GCloudException("type not supported");
			}
			String ipAddress = serverId;
			
			neutronProviderProxy.getClient().networking().lbaasV2().lbPool()
					.createMember(vServerGroupId, Builders.memberV2()
							.protocolPort(job.getInteger("Port"))
							.weight(job.getInteger("Weight"))
							.address(ipAddress)
							.subnetId(subnetId)
							.build());
		}
		log.debug("CreateLbPoolMemeber end...");
	}
	
	private void waitForLbPendingUpdate(String lbId){
		if(StringUtils.isBlank(lbId)){
			throw new GCloudException("缺少参数loadBalanceId");
		}
		
		Long beginTime = System.currentTimeMillis(),
			 timeout = 20000L; // 超时时间为20s
		LoadBalancerV2 loadbalancer = null;
		while (true) {
			if ((System.currentTimeMillis() - beginTime) > timeout) {
				throw new GCloudException("批量添加超时");
			}
			
			loadbalancer = neutronProviderProxy.getClient().networking().lbaasV2().loadbalancer().get(lbId);
			if (loadbalancer != null) {
				String lbStatus = loadbalancer.getProvisioningStatus().name();
				//log.debug("loadbalancer.getProvisioningStatus is " + lbStatus);
				if (!StringUtils.equalsIgnoreCase(lbStatus, LbProvisioningStatus.PENDING_UPDATE.name())) {
					break;
				}
			} else {
				throw new GCloudException("负载均衡不存在");
			}
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

	public void removeVServerGroupBackendServers(String vServerGroupId,String backendServers) {
		if (StringUtils.isNotBlank(backendServers)) {
			JSONArray jArray = JSONArray.parseArray(backendServers);
			for (int i = 0; i < jArray.size(); i++) {
				JSONObject job = jArray.getJSONObject(i);
				ActionResponse response = neutronProviderProxy.getClient().networking().lbaasV2().lbPool().deleteMember(vServerGroupId, job.getString("MemberId"));
				if(!response.isSuccess()) {
					//throw new RuntimeException(response.getFault(),String.valueOf(response.getCode()));
				}
			}
		}
	}

	public void setVServerGroupBackendServers(String vServerGroupId,String backendServers) {
		if (StringUtils.isBlank(backendServers)) {
			throw new GCloudException("backendServers is blank");
		}
		
		LbPoolV2 pool = neutronProviderProxy.getClient().networking().lbaasV2().lbPool().get(vServerGroupId);
		
		String lbId = pool.getLoadbalancers().get(0).getId();
		
		JSONArray jArray = JSONArray.parseArray(backendServers);
		for (int i = 0; i < jArray.size(); i++) {
			JSONObject job = jArray.getJSONObject(i);
			
			waitForLbPendingUpdate(lbId);
			
			neutronProviderProxy.getClient().networking().lbaasV2().lbPool()
					.updateMember(vServerGroupId, job.getString("MemberId"), Builders.memberV2Update()
							.adminStateUp(true)
							.weight(job.getInteger("Weight"))
							.build());
		}
	}


	@Override
	public ResourceType resourceType() {
		// TODO Auto-generated method stub
		return ResourceType.VSERVER_GROUP;
	}


	@Override
	public ProviderType providerType() {
		// TODO Auto-generated method stub
		return ProviderType.NEUTRON;
	}
}
